﻿using System;
using System.Collections.Generic;
using System.Linq;
using VisionarySystems.TimeRegi.Controller;
using VisionarySystems.TimeRegi.Database;
using VisionarySystems.TimeRegi.Model.TimePeriods;
using VisionarySystems.TimeRegi.Registry;
using VisionarySystems.TimeRegi.View;
using VisionarySystems.TimeRegi.View.WorkTimeCalculator;

namespace VisionarySystems.TimeRegi.Model
{
	public class TimeRegistrationViewModel : ICurrentTimeHandler
	{
		#region private members

		private TimePeriodModel m_TimePeriodModel;
		private SummaryGenerator m_SummaryGenerator;
		private BreakTimeCalculator m_BreakTimeCalculator;
		private IWorkTimeCalculator m_WorkTimeCalculator;
		private WorkTimeOverviewGenerator m_WorkTimeOverviewGenerator;
		private ICurrentTimeHandler m_CurrentTimeHandler;
		private DataLayer m_DatabaseLayer;
		private bool m_TimeSlotSaved;
		private bool m_Closing = false;
		private AutomaticRegistrationController m_AutomaticRegistrationController;

		#endregion

		#region events

		public event Action<int> TimeSlotDeleted;

		public event Action TimeSlotsChanged;

		#endregion

		#region Ctor

		public TimeRegistrationViewModel(TimePeriodModel TimePeriodModel)
		{
			m_TimePeriodModel = TimePeriodModel;

			Init(RegistryLayer.BreakPerDay, RegistryLayer.OverTimeStartDate);
		}

		public IEnumerable<ITimePeriode> TimePeriodModel
		{
			get { return m_TimePeriodModel; }
		}

		private void Init(TimeSpan BreakPerDay, DateTime OverTimeStartDate)
		{
			m_CurrentTimeHandler = new CurrentTimeHandler(DateTime.Now);

			//Create AutomaticRegistrationController
			m_AutomaticRegistrationController = new AutomaticRegistrationController(RegistryLayer.RegisterNoMoveTime,  this);
			m_AutomaticRegistrationController.ProgramUseStateChanged += OnProgramUseStateChanged;

			// Connect to database
			m_DatabaseLayer = new DataLayer();
			m_DatabaseLayer.CheckOrCreateDatabase();
			TimeSlotDeleted += m_DatabaseLayer.OnDeleteTimeSlot ;

			m_BreakTimeCalculator = new BreakTimeCalculator(BreakPerDay);
			// Init Calculator
			m_WorkTimeCalculator = new WorkTimeCalculatorFromTimeSlots(m_BreakTimeCalculator, OverTimeStartDate,
																	   m_CurrentTimeHandler);

			m_WorkTimeOverviewGenerator = new WorkTimeOverviewGenerator(m_WorkTimeCalculator);
			m_SummaryGenerator = new SummaryGenerator(m_WorkTimeCalculator, this, m_BreakTimeCalculator);

			FillValuesFromDataLayer();
		}

		private void OnProgramUseStateChanged(UseState CurrentState, DateTime LastTimeUsed)
		{
			switch (CurrentState)
			{
				case UseState.InUse:
					SetStartTime(DateTime.Now);
					//Reinitalize WorkTime Calculator
					//Init(RegistryLayer.BreakPerDay);// Reinitalize
					break;
				case UseState.Unused:
					SaveCurrentTimeSlot(LastTimeUsed);
					SetStartTime(DateTime.MaxValue);
					break;
				default:
					break;
			}
		}

		#endregion

		#region public methods

		public ITimePeriode GetTimePeriode(TimePeriodeID TimePeriodeID)
		{
			return m_TimePeriodModel.GetTimePeriode(TimePeriodeID);
		}

		public int DeleteTimePeriode(ITimePeriode Periode)
		{
			//List<TimeSlot> deletedTimeSlots = new List<TimeSlot>(m_TimePeriodModel.TimeSlots());
			var deletedTimeSlots = (from slot in m_TimePeriodModel.TimeSlots()
											  where slot.ID == Periode.ID
											  select slot).ToList();

			foreach (TimeSlot timeSlot in deletedTimeSlots)
				OnTimeSlotDeleted(timeSlot.Idx); // Delete slots in DataLayer

			m_TimePeriodModel.DeleteTimePeriode(Periode);
			return deletedTimeSlots.Count;
		}

		#endregion

		#region private methods

		private void OnTimeSlotDeleted(int Idx)
		{
			if (TimeSlotDeleted != null)
				TimeSlotDeleted(Idx);
		}

		private void FillValuesFromDataLayer()
		{
			UpdateTimePeriodeController();
			m_WorkTimeCalculator.FillValues(m_TimePeriodModel);
		}

		private void UpdateTimePeriodeController()
		{
			List<TimeSlot> timeSlotList = DataLayer.LoadAllTimeSlots();
			m_TimePeriodModel.Add(timeSlotList);
			OnTimeSlotsChanged();
		}

		private void OnTimeSlotsChanged()
		{
			if (TimeSlotsChanged != null)
				TimeSlotsChanged();
		}

		#endregion

		public string GetSummary()
		{
			return m_SummaryGenerator.Generate();
		}

		public void SetStartTime(DateTime NewStartTime)
		{
			m_CurrentTimeHandler.StartTime = NewStartTime;
			m_TimeSlotSaved = false;
		}

		public void SaveCurrentTimeSlot(DateTime LastTimeUsed)
		{
			int newIdx;
			m_TimeSlotSaved = DataLayer.SaveTimeSlot(m_CurrentTimeHandler.StartTime, LastTimeUsed, TimeSlotTypes.WorkTime, m_TimeSlotSaved, out newIdx);
		}

		public void ApplicationClosing()
		{
			m_Closing = true;
			SaveCurrentTimeSlot(m_CurrentTimeHandler.CurrentTime);
		}

		public void ShutDown()
		{
			SaveCurrentTimeSlot(m_CurrentTimeHandler.CurrentTime);
		}

		public string GetOverview()
		{
			return m_WorkTimeOverviewGenerator.GenerateOverview();
		}

		public void StartPause()
		{
			m_AutomaticRegistrationController.ProgramUseState = UseState.Unused; // Force the OnProgramUseStateChanged event to fire
		}

		public void SaveTimeSlot(TimeSlot NewTimeSlot)
		{
			int result;
			DataLayer.SaveTimeSlot(NewTimeSlot.Start, NewTimeSlot.Stop, TimeSlotTypes.WorkTime, false, out result);
		}

		public void UpdateTimeSlot(TimeSlot Slot)
		{
			// Delete Slot
			DataLayer.DeleteTimeSlot(Slot.Idx);
			m_TimePeriodModel.DeleteTimeSlot(Slot.Idx);

			// Add it again
			SaveTimeSlot(Slot);
			m_TimePeriodModel.Add(new List<TimeSlot>(Slot));
		}

		public DateTime StartTime
		{
			get
			{
				if (m_AutomaticRegistrationController.ProgramUseState == UseState.InUse)
					return m_CurrentTimeHandler.StartTime;
				else
					return m_CurrentTimeHandler.CurrentTime;

			}
			set { m_CurrentTimeHandler.StartTime = value; }
		}

		public DateTime CurrentTime { get { return m_CurrentTimeHandler.CurrentTime; } }

		public DateTime CurrentDate { get { return m_CurrentTimeHandler.CurrentDate; } }

		public void Update()
		{
			FillValuesFromDataLayer();
		}
	}
}